package cn.yx.common.mybatis.service;

import cn.yx.common.core.mapping.DtoDoMapping;
import cn.yx.common.core.pojo.PageDto;
import cn.yx.common.core.pojo.singletable.SingleCond;
import cn.yx.common.core.pojo.singletable.SingleDto;
import cn.yx.common.core.service.BaseService;
import cn.yx.common.core.util.reflect.ReflectUtil;
import cn.yx.common.mybatis.constant.service.PojoPropertyConstant;
import cn.yx.common.mybatis.mapper.BasePlusMapper;
import cn.yx.common.mybatis.pojo.SingleDo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.util.List;

/**
 * <p>BaseService接口的MyBatis-plus实现类，继承自MyBatis-plus的ServiceImpl</p>
 *
 * @param <Do>     DO类
 * @param <Dto>    DTO类
 * @param <Cond>   Cond类
 * @param <Id>     ID主键值类
 * @param <Mapper> Mapper数据访问类
 * @author Wgssmart
 */
public abstract class AbstractMybatisPlusService<Do extends SingleDo,
        Dto extends SingleDto,
        Cond extends SingleCond,
        Id extends Serializable,
        Mapper extends BasePlusMapper<Do, Cond, Id>>
        extends ServiceImpl<Mapper, Do>
        implements BaseService<Dto, Cond, Id> {

    private final BasePlusMapper<Do, Cond, Id> mapper;
    private final DtoDoMapping<Do, Dto> mapping;

    private MybatisPlusWrapperService<Do, Cond> wrapperService;

    public AbstractMybatisPlusService(BasePlusMapper<Do, Cond, Id> mapper, DtoDoMapping<Do, Dto> mapping) {
        this.mapper = mapper;
        this.mapping = mapping;
    }

    @Autowired
    public void setWrapperService(MybatisPlusWrapperService<Do, Cond> wrapperService) {
        this.wrapperService = wrapperService;
    }

    @Override
    public boolean save(Dto dto) {
        return super.save(mapping.dtoToDo(dto));
    }

    @Override
    public boolean save(List<Dto> dtoList) {
        return super.saveBatch(mapping.dtoToDo(dtoList));
    }

    @Override
    public boolean save(List<Dto> dtoList, int batchSize) {
        return super.saveBatch(mapping.dtoToDo(dtoList), batchSize);
    }

    @Override
    public boolean saveOrUpdate(Dto dto) {
        return super.saveOrUpdate(mapping.dtoToDo(dto));
    }

    @Override
    public boolean saveOrUpdate(Dto dto, Cond updateCond) {
        Do entity = mapping.dtoToDo(dto);
        return super.saveOrUpdate(entity, wrapperService.buildUpdateWrapper(entity, updateCond, false));
    }

    @Override
    public boolean saveOrUpdate(List<Dto> dtoList) {
        return super.saveOrUpdateBatch(mapping.dtoToDo(dtoList));
    }

    @Override
    public boolean saveOrUpdate(List<Dto> dtoList, int batchSize) {
        return super.saveOrUpdateBatch(mapping.dtoToDo(dtoList), batchSize);
    }

    @Override
    public boolean remove(Id id) {
        return super.removeById(id);
    }

    @Override
    public boolean remove(List<Id> idList) {
        return super.removeByIds(idList);
    }

    @Override
    public boolean remove(Cond deleteCond) {
        return super.remove(wrapperService.buildCondWrapper(deleteCond));
    }

    @Override
    public boolean update(Dto dto) {
        currentVersion(dto);
        return super.updateById(mapping.dtoToDo(dto));
    }

    @Override
    public boolean update(List<Dto> dtoList) {
        return super.updateBatchById(mapping.dtoToDo(dtoList));
    }

    @Override
    public boolean update(List<Dto> dtoList, int batchSize) {
        return super.updateBatchById(mapping.dtoToDo(dtoList), batchSize);
    }

    @Override
    public boolean update(Dto dto, Cond updateCond) {
        currentVersion(dto);
        Do entity = mapping.dtoToDo(dto);
        return super.update(entity, wrapperService.buildUpdateWrapper(entity, updateCond, false));
    }

    @Override
    public boolean updateAllColumn(Dto dto) {
        currentVersion(dto);
        return super.update(wrapperService.buildUpdateWrapper(mapping.dtoToDo(dto), true));
    }

    @Override
    public boolean updateAllColumn(List<Dto> dtoList) {
        return false;
    }

    @Override
    public boolean updateAllColumn(List<Dto> dtoList, int batchSize) {
        return false;
    }

    @Override
    public boolean updateAllColumn(Dto dto, Cond updateCond) {
        currentVersion(dto);
        Do entity = mapping.dtoToDo(dto);
        return super.update(entity, wrapperService.buildUpdateWrapper(entity, updateCond, true));
    }

    @Override
    public Dto getOne(Id id) {
        return mapping.doToDto(super.getById(id));
    }

    @Override
    public Dto getOne(Cond selectCond) {
        return mapping.doToDto(super.getOne(wrapperService.buildCondWrapper(selectCond)));
    }

    @Override
    public Dto getOne(Cond selectCond, boolean throwEx) {
        return mapping.doToDto(super.getOne(wrapperService.buildCondWrapper(selectCond), throwEx));
    }

    @Override
    public List<Dto> listAll() {
        return mapping.doToDto(super.list());
    }

    @Override
    public List<Dto> listAll(Cond selectCond) {
        return mapping.doToDto(super.list(wrapperService.buildCondWrapper(selectCond)));
    }

    @Override
    public List<Dto> listAll(List<Id> idList) {
        return mapping.doToDto(super.listByIds(idList));
    }

    @Override
    public PageDto<Dto> page(Cond selectCond) {
        Integer pageNum = ReflectUtil.invokeGetter(selectCond, PojoPropertyConstant.PAGE_NUM_PROPERTY, Integer.class);
        Integer pageSize = ReflectUtil.invokeGetter(selectCond, PojoPropertyConstant.PAGE_SIZE_PROPERTY, Integer.class);
        Page<Do> page = super.page(new Page<>(pageNum, pageSize), wrapperService.buildCondWrapper(selectCond));
        PageDto<Dto> pageDto = new PageDto<>();
        pageDto.setTotal(page.getTotal());
        pageDto.setResults(mapping.doToDto(page.getRecords()));
        return pageDto;
    }

    @Override
    public long count() {
        return super.count();
    }

    @Override
    public long count(Cond selectCond) {
        return super.count(wrapperService.buildCondWrapper(selectCond));
    }

    /**
     * 获取DTO版本号
     *
     * @param dto
     */
    @SuppressWarnings({"unchecked"})
    private void currentVersion(Dto dto) {
        Id id = (Id) ReflectUtil.invokeGetter(dto, PojoPropertyConstant.KEY_PROPERTY);
        Integer version = mapper.selectVersionById(id);
        ReflectUtil.invokeSetter(dto, PojoPropertyConstant.VERSION_PROPERTY, version);
    }
}
